/* Copyright Thanh-Trung Pham, 2015 */
package jp.ac.uaizu.control;

import jp.ac.jaist.control.RMPIController;
import jp.ac.jaist.input.RMPIIAWrapper;
import jp.ac.uaizu.message.SerCompMediatorException;
import jp.ac.uaizu.message.SerCompMessageResource;
import jp.ac.uaizu.message.SerCompMessageUtility;
import jp.ac.uaizu.translation.InputModel;

import java.util.*;

/**
 * @author Thanh-Trung Pham
 */
public class SerCompPredictionHelper {
    private List<SerCompPredictionWorker> m_oPredictionWorkers;
    private boolean m_isStopped = false;
    private LinkedList<SerCompPredictionWork> m_oWorkQueue = new LinkedList<SerCompPredictionWork>();

    private Map<String, SerCompPredictionWork> m_oWorkCache = new HashMap<String, SerCompPredictionWork>();

    /**
     * The constructor.
     *
     * @param numberOfWorker the number of prediction workers.
     */
    public SerCompPredictionHelper(int numberOfWorker) {
        m_oPredictionWorkers = new ArrayList<SerCompPredictionWorker>(numberOfWorker);

        for (int i = 0; i < numberOfWorker; i++) {
            m_oPredictionWorkers.add(new SerCompPredictionWorker("PredictionWorker" + i));
        }

        for (SerCompPredictionWorker oPredictionWorker : m_oPredictionWorkers) {
            oPredictionWorker.start();
        }

    }

    /**
     * Stop all the workers.
     */
    public synchronized void stop() {
        m_isStopped = true;

        for (SerCompPredictionWorker oPredictionWorker : m_oPredictionWorkers) {
            oPredictionWorker.stopWorker();
        }
    }

    /**
     * Get the stopped state.
     *
     * @return the stopped state.
     */
    public synchronized boolean isStopped() {
        return m_isStopped;
    }

    /**
     * Add a prediction work to the work queue.
     *
     * @param predictionWork the prediction work.
     */
    private synchronized void addPredictionWorkToWorkQueue(SerCompPredictionWork predictionWork) {
        predictionWork.setStateQueued();
        m_oWorkQueue.addLast(predictionWork);
        notify();
    }

    /**
     * Add a prediction work to the work cache.
     *
     * @param predictionWork the prediction work.
     */
    private void addPredictionWorkToWorkCache(SerCompPredictionWork predictionWork) {
        m_oWorkCache.put(predictionWork.getPredictionID(), predictionWork);

    }


    /**
     * Get a prediction work from the work cache.
     *
     * @param predictionID the prediction ID.
     * @return the prediction work with the prediction ID.
     */
    public SerCompPredictionWork getPredictionWorkFromWorkCache(String predictionID) {
        return m_oWorkCache.get(predictionID);
    }

    /**
     * Get a prediction work from the work queue.
     *
     * @return the prediction work.
     * @throws InterruptedException if an error occurs.
     */
    private synchronized SerCompPredictionWork getPredictionWorkFromWorkQueue() throws InterruptedException {
        while (m_oWorkQueue.isEmpty()) {
            wait();
        }

        return m_oWorkQueue.removeFirst();
    }

    /**
     * Synchronously run the prediction work.
     *
     * @param predictionWork the prediction work.
     */
    public void runPrediction(SerCompPredictionWork predictionWork) {
        InputModel oInputModel = predictionWork.getInputModel();

        RunConfigurationSettings oRunConfigurationSettings = oInputModel.getRunConfigurationSettings();
        if(oRunConfigurationSettings.isAnalyticalCalculationRequired()){
            RMPIController oRMPIController = RMPIController.newInstance();
            RMPIIAWrapper oResultRMPIIAWrapper = oRMPIController.conductReliabilityPrediction(predictionWork.getRMPIInputModeler());

            predictionWork.setRMPIIAWrapper(oResultRMPIIAWrapper);
        }else{
            SerCompSimulationHelper oSerCompSimulationHelper = new SerCompSimulationHelper(predictionWork);
            oSerCompSimulationHelper.runSimulations();
        }



    }

    /**
     * Asynchronously start the prediction work.
     *
     * @param predictionWork the prediction work.
     * @throws SerCompMediatorException if there is an error.
     */
    public void startPrediction(SerCompPredictionWork predictionWork) throws SerCompMediatorException {
        if (m_isStopped) {
            throw new SerCompMediatorException(SerCompMessageUtility.getResourceMessage(SerCompMessageResource.MEDIATOR_INVALID_PREDICTION_RESULTS));
        }

        addPredictionWorkToWorkCache(predictionWork);
        addPredictionWorkToWorkQueue(predictionWork);
    }

    /**
     * Remove the prediction work.
     * @param predictionID the prediction ID.
     */
    public void removePredictionWork(String predictionID) {
        SerCompPredictionWork oPredictionWork = removePredictionWorkFromWorkCache(predictionID);
        if(oPredictionWork !=null){
            removePredictionWorkFromWorkQueue(oPredictionWork);
        }

    }

    /**
     * Remove the prediction work from the work queue.
     * @param predictionWork the prediction work.
     */
    private synchronized void removePredictionWorkFromWorkQueue(SerCompPredictionWork predictionWork) {
        m_oWorkQueue.remove(predictionWork);
    }

    /**
     * Remove the prediction work from the work cache.
     * @param predictionID the prediction ID.
     * @return the prediction work.
     */
    private SerCompPredictionWork removePredictionWorkFromWorkCache(String predictionID) {
        return m_oWorkCache.remove(predictionID);
    }

    /**
     * Remove all prediction works.
     */
    public void removeAllPredictionWorks() {
        removeAllPredictionWorksFromWorkCache();
        removeAllPredictionWorksFromWorkQueue();
    }

    /**
     * Remove all prediction works from the work queue.
     */
    private synchronized void removeAllPredictionWorksFromWorkQueue() {
        m_oWorkQueue.clear();
    }

    /**
     * Remove all the prediction works from the work cache.
     */
    private void removeAllPredictionWorksFromWorkCache() {
        m_oWorkCache.clear();
    }

    /**
     * Get all prediction IDs.
     * @return all the prediction IDs.
     */
    public String[] getAllPredictionIDs() {
        return m_oWorkCache.keySet().toArray(new String[m_oWorkCache.size()]);
    }


    private class SerCompPredictionWorker extends Thread {
        private boolean m_isStopped;

        /**
         * The constructor.
         *
         * @param name the name for the worker.
         */
        public SerCompPredictionWorker(String name) {
            super(name);
        }

        /**
         * Stop the worker.
         */
        public synchronized void stopWorker() {
            m_isStopped = true;

            interrupt();
        }

        @Override
        public void run() {
            while (true) {
                if (m_isStopped) {
                    return;
                }

                try {
                    SerCompPredictionWork oPredictionWork = getPredictionWorkFromWorkQueue();

                    oPredictionWork.setStateRunning();
                    runPrediction(oPredictionWork);
                    oPredictionWork.setStateCompleted();
                } catch (InterruptedException oEx) {
                    oEx.printStackTrace();
                    return;
                }
            }
        }
    }

}


